/*
*   HTTP服务器模块
*/
#ifndef __BUC_SERVICE__
#define __BUC_SERVICE__

#include "datamanage.hpp"
#include "httplib.h"
#include <sstream>

namespace cloud
{
    class Service 
    {
    private:
        size_t _server_port;           // 服务器端口
        std::string _service_ip;       // 服务器ip
        std::string _download_prefix;  // 下载请求前缀 当路径中含有该前缀则表示是一个下载请求
        httplib::Server _server;       // http服务器

    private:
        static void Upload(const httplib::Request& req,httplib::Response& rsp) //上传请求处理函数
        {
            //没有文件上传 返回错误
            if(req.has_file("file") == false)
            {
                rsp.status = 412;
                return;
            }

            const auto& file = req.get_file_value("file");
            //因为Linux上是utf-8编码，而Windows是gbk引入编码转换修复文件识别bug
            std::string realpath = Config::GetInstance()->GetBackDir() + EncodingConver::GBKToUTF8(FileUtil(file.filename).FileName());
            if(realpath == "./backdir/") //上传无效路径
            {
                rsp.status = 412;
                rsp.body = "FILE UPLOAD ERROR!";
                rsp.set_header("Content-Type","text/plain"); 
                return;
            } 
            FileUtil realfile(realpath); //上传已存在的文件会刷新文件
            if(realfile.SetContent(file.content) == false)
            {
                std::cout<<"Upload(): setcontent error!"<<std::endl;
                return;
            }
            BackupInfo info;
            info.InitBackupinfo(realpath);
            //如果是已上传的文件 则进行更新 否则进行新增
            if(DataManager::GetInstance()->IsExists(info)) DataManager::GetInstance()->Update(info);
            else DataManager::GetInstance()->Insert(info);

            rsp.status = 200;
            rsp.body = "UPLOAD SUCCESS!";
            rsp.set_header("Content-Type","text/plain"); 
            return;
        }

        static void ListShow(const httplib::Request& req,httplib::Response& rsp) //展示页面获取请求
        {
            //1.获取所有备份文件信息
            std::vector<BackupInfo> arr;
            DataManager::GetInstance()->GetAll(&arr);
            //2.组织html
            std::stringstream html_text;
            html_text<<"<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /><title>Download</title></head><body><h1>Download</h1><hr><table>";
            if(!arr.empty()) html_text<<"<p>&emsp;文件名称&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;最后修改时间&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;最后访问时间&emsp;&emsp;&emsp;&emsp;文件大小</p>";
            for(auto& info:arr)
            {
                html_text << "<tr><td><a href='" << info.url_path<< "'>" << FileUtil(info.real_path).FileName() << "</a> &nbsp;|&nbsp; </td>";
                html_text << "<td align='right'>" << ctime(&info.mtime) << " &nbsp;|&nbsp; </td>";
                html_text << "<td align='right'>" << ctime(&info.atime) << " &nbsp;|&nbsp; </td>";
                html_text << "<td align='right'>" << info.fsize << "KB" << "</td></tr>";
            }
            //如果没有文件则标明 文件列表为空
            if(arr.empty()) html_text << "<p>There are no backup files!</p>";
            html_text << "</table></body></html>";
            //将网页写入
            rsp.body = html_text.str();
            //标注内容类型为 文本html
            rsp.set_header("Content-Type","text/html");
            return;
        }

        //目前存在的问题
        //文件访问和修改后 信息不能同步 导致文件内容修改后断点续传正常(在文件大小相同的情况下)
        //由于服务器带宽问题，可能传输时间会超出热点文件的判断时间导致正在传输的文件被压缩 客户端那边会下载异常 对于长时间传输需要进行优化
        //目前建议传输时间设置的时间计算方式: 最大文件大小/服务器每秒平均发送量(Byte) 所得的时间(秒)
        static void Download(const httplib::Request& req,httplib::Response& rsp) //文件下载处理函数
        {
            //1.获取客户端资源路径path req.path
            //2.根据文件路径，获取文件备份信息
            BackupInfo info;
            DataManager::GetInstance()->GetOneByUrl(req.path,&info);
            std::string etag = FileUtil(req.path).FileName() + "-" + std::to_string(info.fsize) + "-" + std::to_string(info.mtime);
            //3.判断文件是否被压缩，如果被压缩要先解压
            //4.删除压缩包，修改备份信息
            if(info.pack_flag)
            {
                FileUtil unpackfile(info.pack_path);
                if(unpackfile.UnCompress(info.real_path) == false)
                {
                    std::cout<<"Service: Download() UnCompress error!"<<std::endl;
                    rsp.body = "File Download Error!";
                    rsp.set_header("Content-Type","text/plain");
                    rsp.status = 500;
                    return;
                }
                info.pack_flag = false;
                unpackfile.FileRemove();
                info.atime = FileUtil(info.real_path).FileAccTime();
                DataManager::GetInstance()->Update(info);
            }
            //5.读取文件数据，放入rsp.body中
            FileUtil realfile(info.real_path);
            rsp.status = 200; //先设置默认200
            //识别If-Range头字段是否存在 如果存在为断点续传
            if(req.has_header("If-Range"))
            {
                std::string reqetag = req.get_header_value("If-Range");
                if(reqetag == etag) rsp.status = 206; //断点续传设置为206
            }
            if(realfile.GetContent(&rsp.body) == false)
            {
                std::cout<<"Service: Download() GetContent error!"<<std::endl;
                rsp.body = "File Download Error!";
                rsp.set_header("Content-Type","text/plain");
                rsp.status = 500;
                return;
            }
            //6.设置响应头部字段，ETag，Accept-Ranges:bytes,Content-Type
            rsp.set_header("Accept-Ranges","bytes");
            rsp.set_header("ETag",etag.c_str());
            rsp.set_header("Content-Type","application/octet-stream");
            return;
        }

    public:
        Service()
        {
            Config* conf = Config::GetInstance();
            _server_port = conf->GetServerPort();
            _service_ip = conf->GetServerIP();
            _download_prefix = conf->GetDownloadPrefix();
        }
        bool RunModule() //启动函数
        {
            _server.Post("/upload",Upload);
            _server.Get("/listshow",ListShow);
            _server.Get("/",ListShow); //默认列表查看
            std::string download_url = _download_prefix + "(.*)"; //匹配任意字符串
            _server.Get(download_url,Download);
            if(_server.listen(_service_ip.c_str(),_server_port) == false)
            {
                std::cout<<"RunModule(): listen error!"<<std::endl;
                return false;
            }
            return true;
        }

    };
}


#endif
